#include "VarifyGrpcClient.h"

GetVarifyRsp VerifyGrpcClient::GetVarifyCode(std::string email)
{
	ClientContext context;						//客户端环境
	GetVarifyRsp reply;							//回包

	//比如这里，就是使用set属性直接设置进去
	GetVarifyReq request;						//请求
	request.set_email(email);					//设置email


	auto stuub = pool_->getConnection();
	Status status = stuub->GetVarifyCode(&context, request, &reply);		//通过它发送到RPC服务端，进行调用

	if (status.ok()) {
		pool_->returnConnection(std::move(stuub));
		return reply;
	}
	else {
		pool_->returnConnection(std::move(stuub));
		reply.set_error(ErrorCodes::RPCFailed);
		return reply;
	}
}

//池本身需要保护，所以要一把锁进行保护
//unique看起来私塾也是安全的

VerifyGrpcClient::VerifyGrpcClient() {
	auto& gCfgMgr = ConfigMgr::Inst();
	std::string host = gCfgMgr["VarifyServer"]["Host"];
	std::string port = gCfgMgr["VarifyServer"]["Port"];
	pool_.reset(new RPConPool(5, host, port));
	std::shared_ptr<Channel> channel = grpc::CreateChannel("127.0.0.1:50051", grpc::InsecureChannelCredentials());
	stub_ = VarifyService::NewStub(channel);
}



RPConPool::RPConPool(size_t poolSize, std::string host, std::string port) : poolSize_(poolSize), host_(host), port_(port), b_stop_(false)
{
	for (size_t i = 0; i < poolSize_; ++i) {

		std::shared_ptr<Channel> channel = grpc::CreateChannel(host + ":" + port,
			grpc::InsecureChannelCredentials());

		connections_.push(VarifyService::NewStub(channel));
	}
}

RPConPool::~RPConPool()
{
	std::lock_guard<std::mutex> lock(mutex_);
	Close();
	while (!connections_.empty()) {
		connections_.pop();
	}
}

std::unique_ptr<VarifyService::Stub> RPConPool::getConnection()
{
	std::unique_lock<std::mutex> lock(mutex_);
	cond_.wait(lock, [this] {
		if (b_stop_) {
			return true;
		}
		return !connections_.empty();
		});
	//如果停止则直接返回空指针
	if (b_stop_) {
		return  nullptr;
	}
	auto context = std::move(connections_.front());
	connections_.pop();
	return context;
}

void RPConPool::returnConnection(std::unique_ptr<VarifyService::Stub> context)
{
	std::lock_guard<std::mutex> lock(mutex_);
	if (b_stop_) {
		return;
	}
	connections_.push(std::move(context));
	cond_.notify_one();
}

void RPConPool::Close()
{
	b_stop_ = true;
	cond_.notify_all();
}


//这里创建了两种池，而池要能被多线程复用的话，就要考虑会不会破坏池的结构
//归还和拿走，是会破坏的，所以如果有这两种操作的话，那就要使用条件按变量和所进行保护
//还有一种池，只有读的操作，读出来的结构本身线程安全，也是可以的